package test.base.medium;

import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import static org.bytedeco.ffmpeg.global.avformat.*;
import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avutil.*;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVIOContext;
import org.bytedeco.ffmpeg.avformat.AVOutputFormat;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.avutil.AVDictionary;
import org.bytedeco.ffmpeg.avutil.AVRational;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.PointerPointer;
import hyl.core.MyFun;

public class ffmpegtest4 {
	final static String rtspPath = "rtsp://admin:nt123321@192.168.0.2:554/h264/1/sub/av_stream";
	final static String rtmpPath = "rtmp://192.168.0.147:1935/hyl/te1";
	public static void main(String[] arg) {
		showimg2(rtspPath,rtmpPath);
		//showimg2(rtspPath,rtmpPath);

	}
	static void printf(String ... m) {
		System.out.println(MyFun.join(m));
	}
	private static void showimg1(String ...argv) {
		{
		    AVOutputFormat ofmt = null;
		    AVFormatContext ifmt_ctx = new AVFormatContext(null), ofmt_ctx = new AVFormatContext(null);
		    AVPacket pkt;
		    String in_filename, out_filename;
		    int ret, i;
		    int stream_index = 0;
		    int stream_mapping_size = 0;
		    int videoindex=-1;
			int frame_index=0;
			long start_time=0;
		    in_filename  = argv[0];
		    out_filename = argv[1];
		   // av_register_all();
			//Network
			avformat_network_init();

		    AVDictionary dict = new AVDictionary(null);
		    av_dict_set(dict, "rtsp_transport", "tcp", 0);
		    // 1. 打开输入
		    // 1.1 读取文件头，获取封装格式相关信息
		    if ((ret = avformat_open_input(ifmt_ctx, in_filename, null,dict)) < 0) {
		        printf("Could not open input file '%s'", in_filename);
		        return;
		    }
		    
		    // 1.2 解码一段数据，获取流相关信息
		    if ((ret = avformat_find_stream_info(ifmt_ctx, (PointerPointer<Pointer>) null)) < 0) {
		        printf("Failed to retrieve input stream information");
		        return;
		    }

		    av_dump_format(ifmt_ctx, 0, in_filename, 0);

		    // 2. 打开输出
		    // 2.1 分配输出ctx
		    boolean push_stream = false;
		    String ofmt_name = null;
		    if (out_filename.startsWith("rtmp://")) {
		        push_stream = true;
		        ofmt_name = "flv";
		    }
		    else if (out_filename.startsWith("udp://")) {
		        push_stream = true;
		        ofmt_name = "mpegts";
		    }
		    else {
		        push_stream = false;
		        ofmt_name = null;
		    }
		    avformat_alloc_output_context2(ofmt_ctx, null, ofmt_name, out_filename);
		    if (null==ofmt_ctx) {
		        printf("Could not create output context\n");
		        return;
		    }

		    
		    for (i = 0; i < ifmt_ctx.nb_streams(); i++) {
		    	AVStream in_stream = ifmt_ctx.streams(i);
		    	if(in_stream.codecpar(). codec_type()==AVMEDIA_TYPE_VIDEO){
					videoindex=i;
					break;
				}

		    }
		  //  Map<Integer,AVStream> streammap=new HashMap<Integer,AVStream> ();
		    int videoidx=0;
		    ofmt = ofmt_ctx.oformat();

		    AVRational frame_rate;
		    double duration;

		    for (i = 0; i < ifmt_ctx.nb_streams(); i++) {
		    	  AVStream out_stream;
		        AVStream in_stream = ifmt_ctx.streams(i);
		        AVCodecParameters in_codecpar = in_stream.codecpar();

		        if (in_codecpar.codec_type() != AVMEDIA_TYPE_AUDIO &&
		            in_codecpar.codec_type() != AVMEDIA_TYPE_VIDEO &&
		            in_codecpar.codec_type() != AVMEDIA_TYPE_SUBTITLE) {		           
		            continue;
		        }

		     

		        // 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
		        out_stream = avformat_new_stream(ofmt_ctx, null);
		        if (null==out_stream) {
		            printf("Failed allocating output stream\n");
		            ret = AVERROR_UNKNOWN;
		            return;
		        }

		        // 2.3 将当前输入流中的参数拷贝到输出流中
		        ret = avcodec_parameters_copy(out_stream.codecpar(), in_codecpar);
		        if (ret < 0) {
		            printf("Failed to copy codec parameters\n");
		            return;
		        }
		        out_stream.codecpar().codec_tag(0) ;
		       // streammap.put(i, out_stream);
		        
		        if ((ofmt_ctx.oformat().flags() & AVFMT_GLOBALHEADER) == AVFMT_GLOBALHEADER) {
		        	out_stream.codec().flags(out_stream.codec().flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
				}

//		        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
//					out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

		    }
		    av_dump_format(ofmt_ctx, 0, out_filename, 1);
		    System.out.println(ofmt.flags() & AVFMT_NOFILE);
		    if (0==(ofmt.flags() & AVFMT_NOFILE)) {    // TODO: 研究AVFMT_NOFILE标志
		        // 2.4 创建并初始化一个AVIOContext，用以访问URL(out_filename)指定的资源
//		        ret = avio_open(ofmt_ctx.pb(), out_filename, AVIO_FLAG_WRITE);
//		        if (ret < 0) {
//		            printf("Could not open output file '%s'", out_filename);
//		            return;
//		        }	
		        AVIOContext pb = new AVIOContext(null);
				if ((ret = avio_open(pb, out_filename, AVIO_FLAG_WRITE)) < 0) {
					 printf("Could not open output file '%s'", out_filename);

				}
				ofmt_ctx.pb(pb);
				System.out.println("打开数据文件");
		    }

		    // 3. 数据处理
		    // 3.1 写输出文件头
		    ret = avformat_write_header(ofmt_ctx, (PointerPointer<Pointer>) null);
		    if (ret < 0) {
		        printf("Error occurred when opening output file\n");
		        return;
		    }
		    start_time=av_gettime();
		    pkt=new AVPacket();
		    System.out.println("-1-");
		  
		    while (true) {
		        AVStream in_stream, out_stream;
		  
		        // 3.2 从输出流读取一个packet
		        ret = av_read_frame(ifmt_ctx, pkt);
	
		        if (ret < 0) {
		            break;
		        }
		        
		        
		      //F等价于av_packet_rescale_ts
				//Simple Write PTS
				if(pkt.pts()==AV_NOPTS_VALUE){
					//Write PTS
					AVRational time_base1=ifmt_ctx.streams(videoindex).time_base();
					//Duration between 2 frames (us)
					double calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx.streams(videoindex).r_frame_rate());
					//Parameters
					pkt.pts((long)((frame_index*calc_duration)/(av_q2d(time_base1)*AV_TIME_BASE)));
					pkt.dts(pkt.pts());
					pkt.duration((long)(calc_duration/(av_q2d(time_base1)*AV_TIME_BASE)));
				}
				//Important:Delay
				if(pkt.stream_index()==videoindex){
					AVRational time_base=ifmt_ctx.streams(videoindex).time_base();
					AVRational time_base_q=av_d2q (1,AV_TIME_BASE);
					long pts_time = av_rescale_q(pkt.dts(), time_base, time_base_q);
					long now_time = av_gettime() - start_time;
					if (pts_time > now_time)
						av_usleep((int)(pts_time - now_time));
		 
				}
		 
				in_stream  = ifmt_ctx.streams(pkt.stream_index());
				out_stream = ofmt_ctx.streams(pkt.stream_index());
				/* copy packet */
				//转换PTS/DTS（Convert PTS/DTS）
				pkt.pts(av_rescale_q_rnd(pkt.pts(), in_stream.time_base(), out_stream.time_base(),(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)));
				pkt.dts(av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), out_stream.time_base(),(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)));
				pkt.duration(av_rescale_q(pkt.duration(), in_stream.time_base(), out_stream.time_base()));

		
		        // 3.3 更新packet中的pts和dts
		        // 关于AVStream.time_base(容器中的time_base)的说明：
		        // 输入：输入流中含有time_base，在avformat_find_stream_info()中可取到每个流中的time_base
		        // 输出：avformat_write_header()会根据输出的封装格式确定每个流的time_base并写入文件中
		        // AVPacket.pts和AVPacket.dts的单位是AVStream.time_base，不同的封装格式AVStream.time_base不同
		        // 所以输出文件中，每个packet需要根据输出封装格式重新计算pts和dts
		       // av_packet_rescale_ts(pkt, in_stream.time_base(), out_stream.time_base());
		        pkt.pos(-1) ;
		        System.out.println("--");
		        // 3.4 将packet写入输出
		        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
		       // ret = av_write_frame(ofmt_ctx, pkt);
		        if (ret < 0) {
		            printf("Error muxing packet\n");
		            break;
		        }
		        av_packet_unref(pkt);
		    }

		    // 3.5 写输出文件尾
		    av_write_trailer(ofmt_ctx);

	
		    avformat_close_input(ifmt_ctx);
		    avformat_free_context(ofmt_ctx);
		
		    if (ofmt_ctx!=null && (0==(ofmt.flags() & AVFMT_NOFILE))) {
		        avio_closep(ofmt_ctx.pb());
		    }
//		    avformat_free_context(ofmt_ctx);
//

//
//		    if (ret < 0 && ret != AVERROR_EOF) {
//		        printf("Error occurred: %s\n", av_err2str(ret));
//		        return 1;
//		    }
//
//		    return 0;
		}

	
	}
	
	private static void showimg2(String ...argv) {
		{
		    AVOutputFormat ofmt = null;
		    AVFormatContext ifmt_ctx = new AVFormatContext(null), ofmt_ctx = new AVFormatContext(null);
		    AVPacket pkt;
		    String in_filename, out_filename;
		    int ret, i;
		   // int stream_index = 0;

		  //  int stream_mapping_size = 0;

		    in_filename  = argv[0];
		    out_filename = argv[1];
		    AVDictionary dict = new AVDictionary(null);
		    //av_dict_set(dict, "rtsp_transport", "tcp", 0);
		    // 1. 打开输入
		    // 1.1 读取文件头，获取封装格式相关信息
		    if ((ret = avformat_open_input(ifmt_ctx, in_filename, null,dict)) < 0) {
		        printf("Could not open input file '%s'", in_filename);
		        return;
		    }
		    
		    // 1.2 解码一段数据，获取流相关信息
		    if ((ret = avformat_find_stream_info(ifmt_ctx, (PointerPointer<Pointer>) null)) < 0) {
		        printf("Failed to retrieve input stream information");
		        return;
		    }

		    av_dump_format(ifmt_ctx, 0, in_filename, 0);

		    // 2. 打开输出
		    // 2.1 分配输出ctx
		    boolean push_stream = false;
		    String ofmt_name = null;
		    if (out_filename.startsWith("rtmp://")) {
		        push_stream = true;
		        ofmt_name = "flv";
		    }
		    else if (out_filename.startsWith("udp://")) {
		        push_stream = true;
		        ofmt_name = "mpegts";
		    }
		    else {
		        push_stream = false;
		        ofmt_name = null;
		    }
		    avformat_alloc_output_context2(ofmt_ctx, null, ofmt_name, out_filename);
		    if (null==ofmt_ctx) {
		        printf("Could not create output context\n");
		      //  ret = AVERROR_UNKNOWN;
		        return;
		    }

	
		
		//    int videoidx=0;
		    ofmt = ofmt_ctx.oformat();

		 //   AVRational frame_rate;
		   // double duration;

		    for (i = 0; i < ifmt_ctx.nb_streams(); i++) {
		    	AVStream out_stream;
		        AVStream in_stream = ifmt_ctx.streams(i);
		        AVCodecParameters in_codecpar = in_stream.codecpar();
		        
		        if (in_codecpar.codec_type() != AVMEDIA_TYPE_AUDIO &&
		            in_codecpar.codec_type() != AVMEDIA_TYPE_VIDEO &&
		            in_codecpar.codec_type() != AVMEDIA_TYPE_SUBTITLE) {		           
		            continue;
		        }

		      //  if ( in_codecpar.codec_type() == AVMEDIA_TYPE_VIDEO) {
		       // 	videoidx=i;
		         //   frame_rate = av_guess_frame_rate(ifmt_ctx, in_stream, null);
		         //   duration = ((frame_rate.num ()>0&& frame_rate.den()>0) ? av_q2d(frame_rate) : 0);
		       // }

		     

		        // 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
		        out_stream = avformat_new_stream(ofmt_ctx, null);
		        if (null==out_stream) {
		            printf("Failed allocating output stream\n");
		            ret = AVERROR_UNKNOWN;
		            return;
		        }

		        // 2.3 将当前输入流中的参数拷贝到输出流中
		        ret = avcodec_parameters_copy(out_stream.codecpar(), in_codecpar);
		        if (ret < 0) {
		            printf("Failed to copy codec parameters\n");
		            return;
		        }
		        out_stream.codecpar().codec_tag(0) ;
		        
		        
		        
		    	// 大部分参数的复制都在这句
				//avcodec_parameters_to_context(ofmt_ctx, in_stream.codecpar());
				
		      //  out_stream.time_base(in_stream.time_base())	;
//				if ((ofmt_ctx.flags() & AVFMT_GLOBALHEADER) == AVFMT_GLOBALHEADER) {
//					ofmt_ctx.flags(ofmt_ctx.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
//				}

			
//				AVRational 宽高比 = inparam.sample_aspect_ratio();
//				视频编码器.sample_aspect_ratio(宽高比);
//				视频输出流.sample_aspect_ratio(宽高比);
//
//				//设置帧率, 调整时间基线
//				AVRational 帧率 = 输入流.r_frame_rate();
//				AVRational supported_framerates = 编码.supported_framerates();
//				if (supported_framerates != null) {
//					int idx = av_find_nearest_q_idx(帧率, supported_framerates);
//					av帧率 = supported_framerates.position(idx);
//				}else
//				{
//					av帧率=帧率;
//				}
//				
//				//不改变帧率字节输出
//				AVRational	time_base = avutil.av_inv_q(av帧率);
//				视频编码器.time_base(time_base);
//				视频输出流.time_base(time_base);
//				视频输出流.codec().time_base(time_base); 
		       // streammap.put(i, out_stream);
		    }
		    av_dump_format(ofmt_ctx, 0, out_filename, 1);
		    System.out.println(ofmt.flags() & AVFMT_NOFILE);
		    if (0==(ofmt.flags() & AVFMT_NOFILE)) {    // TODO: 研究AVFMT_NOFILE标志
		        // 2.4 创建并初始化一个AVIOContext，用以访问URL(out_filename)指定的资源
//		        ret = avio_open(ofmt_ctx.pb(), out_filename, AVIO_FLAG_WRITE);
//		        if (ret < 0) {
//		            printf("Could not open output file '%s'", out_filename);
//		            return;
//		        }	
		        AVIOContext pb = new AVIOContext(null);
				if ((ret = avio_open(pb, out_filename, AVIO_FLAG_WRITE)) < 0) {
					 printf("Could not open output file '%s'", out_filename);

				}
				ofmt_ctx.pb(pb);
				System.out.println("打开数据文件");
		    }

		    // 3. 数据处理
		    // 3.1 写输出文件头
		    ret = avformat_write_header(ofmt_ctx, (PointerPointer<Pointer>) null);
		    if (ret < 0) {
		        printf("Error occurred when opening output file\n");
		        return;
		    }
		    pkt=new AVPacket();
		    System.out.println("-1-");
		    while (true) {
		        AVStream in_stream, out_stream;
		  
		        // 3.2 从输出流读取一个packet
		        ret = av_read_frame(ifmt_ctx, pkt);
	
		        if (ret < 0) {
		            break;
		        }
		        int idx=pkt.stream_index();
		       // System.out.println("-2-");
		        in_stream  = ifmt_ctx.streams(idx);
		        out_stream = ofmt_ctx.streams(idx);
		     
		     //   System.out.println("-3-");
		     //   int codec_type = in_stream.codecpar().codec_type();
//		        if (push_stream && (codec_type == AVMEDIA_TYPE_VIDEO)) {
//		            av_usleep( AV_TIME_BASE);
//		        }
		     //   System.out.println("-4-");
		       // pkt.stream_index(idx);
		   
		     //   System.out.println("-5-");
		        //pkt.pos(-1) ;
		        // 3.3 更新packet中的pts和dts
		        // 关于AVStream.time_base(容器中的time_base)的说明：
		        // 输入：输入流中含有time_base，在avformat_find_stream_info()中可取到每个流中的time_base
		        // 输出：avformat_write_header()会根据输出的封装格式确定每个流的time_base并写入文件中
		        // AVPacket.pts和AVPacket.dts的单位是AVStream.time_base，不同的封装格式AVStream.time_base不同
		        // 所以输出文件中，每个packet需要根据输出封装格式重新计算pts和dts
		        av_packet_rescale_ts(pkt, in_stream.time_base(), out_stream.time_base());
		     
		        System.out.println("--");
		        // 3.4 将packet写入输出
		        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
		       // ret = av_write_frame(ofmt_ctx, pkt);
		        if (ret < 0) {
		            printf("Error muxing packet\n");
		            break;
		        }
		        av_packet_unref(pkt);
		    }

		    // 3.5 写输出文件尾
		    av_write_trailer(ofmt_ctx);

	
		    avformat_close_input(ifmt_ctx);
		    avformat_free_context(ofmt_ctx);
		
		    if (ofmt_ctx!=null && (0==(ofmt.flags() & AVFMT_NOFILE))) {
		        avio_closep(ofmt_ctx.pb());
		    }
//		    avformat_free_context(ofmt_ctx);
//

//
//		    if (ret < 0 && ret != AVERROR_EOF) {
//		        printf("Error occurred: %s\n", av_err2str(ret));
//		        return 1;
//		    }
//
//		    return 0;
		}

	
	}
}
